Introduction
CascadingDropDownExtender is one of the most popular AjaxControlToolkit extenders
The sample that ships with the Toolkit uses the CascadingDropDown with an XML data store. A common question is how to use it with a database
Background
I recently found myself needing to handle a case where I had three drop down lists, and I needed to setup cascading relationship between those:
Using the CascadingDropDown extender really make this a breeze, except that I had additional requirement, the City drop down should be filtered by both MainLand and Country. So, when a MainLand is selected, all the matching Country types are filled as well as all the matching City for the MainLand. When an Country is selected, the City list should contains only the City for this Country type.
At first I tried to simply setup a new extender from City to MainLand, that worked, until I selected a value from the Country list, after which trying to unselect a value would leave me with a disabled drop down.
Notice:For Use this ToolKit Componenet you should Install The AJAX Control Toolkit
Download the AJAX Control Toolkit from The Official Microsoft ASP.NET Site
Add the AJAX Toolkit controls to the Visual Studio Toolbox

In Visual Studio (or Visual Web Developer Express), right-click on the toolbox and select Add Tab

Give the Tab a name
Expand the tab that was just created and right-click on the section and select Choose Items.

The AJAX Toolkit controls will now appear in your toolbox:

Using the Code
In the default.aspx page and add some dropdowns to it:
<dropdownlist id="MainLanddd" runat="server" width="149px"
datasourceid="ods_Continent" datatextfield="Name" datavaluefield="Id" />
<asp:DropDownList ID="MainLanddd" runat="server"
Width="149px" DataSourceID="ods_Continent" DataTextField="Name"
DataValueField="Id">
</asp:DropDownList>
<
<asp:ObjectDataSource ID="ods_Continent" runat="server"
OldValuesParameterFormatString="original_{0}"
SelectMethod="GetContinentByParentId"
TypeName="dsContinentTableAdapters.ContinentTableAdapter">
<SelectParameters>
<asp:Parameter DefaultValue="0" Name="ParentId" Size="4" Type="Int32" />
</SelectParameters>
</asp:ObjectDataSource>
<asp:DropDownList ID="Countrydd" runat="server" Width="150px">
</asp:DropDownList>
<asp:DropDownList ID="Citydd" runat="server" Width="150px">
</asp:DropDownList>
Now, at the top of your ASPX page, register a prefix for the reference to the toolkit:
<%@ Register assembly="AjaxControlToolkit" namespace="AjaxControlToolkit"
tagprefix="cc1" %>
<%@ Register assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" namespace="System.Web.UI" tagprefix="asp" %>
And then add the extender itself:
<cc1:CascadingDropDown ID="CascadingCountry" Category="MainLand"
runat="server" ServicePath="MainLandService.asmx"
ServiceMethod="GetCountryId" TargetControlID="Citydd"
ParentControlID="Countrydd">
</cc1:CascadingDropDown>
<cc1:CascadingDropDown ID="CascadingContinent" Category="MainLand"
runat="server" ServicePath="MainLandService.asmx" ServiceMethod="GetContinentId"
TargetControlID="Countrydd" ParentControlID="MainLanddd">
</cc1:CascadingDropDown>
At runtime, the extender will make callbacks to a web service we specify (which is decorated with the System.Web.Script.Services.ScriptService attribute). In that web service, it expects a WebMethod with the following signature (note that parameter names must match tool):
[WebMethod]
CascadingDropDownNameValue[] GetColorsForModel(
knownCategoryValues,string category)
The knownCategoryValues parameter will return a string containing the currently selected category values, as well as the category to retrieve values for. For example, if the extender is populating the "MainLand" field, you will be passed the values for the "Country" and "City" fields, as well as "Color" to specify the field to return values for.
The CascadingDropDown class has a helper method for unpacking the category values:
CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);
This method will return a StringDictionary containing the name/value pairs of the currently selected values. So imagine you've got a database with tables for the Make (manufacturer), Model, and Color information, and you're accessing that database through a DataSet to which you've added methods for getting each set of values.
The web method to get the available colors for a given model would look like this:
StringDictionary categoryValues =
AjaxControlToolkit.CascadingDropDown.ParseKnownCategoryValuesString(
knownCategoryValues);
int countryID = Convert.ToInt32(categoryValues["MainLand"]);
List<CascadingDropDownNameValue> cascadingValues =
new List<CascadingDropDownNameValue>();
dsContinentTableAdapters.ContinentTableAdapter _continentAdapter =
new dsContinentTableAdapters.ContinentTableAdapter();
foreach (DataRow _row in _continentAdapter.GetContinentByParentId(countryID))
{
cascadingValues.Add(new CascadingDropDownNameValue(_row["Name"].ToString(),
_row["ID"].ToString()));
}
return cascadingValues.ToArray();
So it's simple to return the values. Note that the values are returned as an array of CascadaingDropDownNameValue structures. This structure also includes an isDefaultValue boolean field that allows the specification of which value should be selected when the list is first displayed.
Now let's hook up our extender:
<cc1:CascadingDropDown ID="CascadingCountry" Category="MainLand"
runat="server" ServicePath="MainLandService.asmx"
ServiceMethod="GetCountryId" TargetControlID="Citydd"
ParentControlID="Countrydd">
</cc1:CascadingDropDown>
<cc1:CascadingDropDown ID="CascadingContinent" Category="MainLand"
runat="server" ServicePath="MainLandService.asmx" ServiceMethod="GetContinentId"
TargetControlID="Countrydd" ParentControlID="MainLanddd">
</cc1:CascadingDropDown>
Once you've completed your web service methods, your cascading drop down is complete!
Points of Interest
Notice:
in your Web Config you must add This Tags
<configuration>
<configSections>
<sectionGroup name="system.web.extensions"
type="System.Web.Configuration.SystemWebExtensionsSectionGroup,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35">
<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35">
<section name="scriptResourceHandler"
type="System.Web.Configuration.ScriptingScriptResourceHandlerSection,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" requirePermission="false"
allowDefinition="MachineToApplication"/>
<sectionGroup name="webServices"
type="System.Web.Configuration.ScriptingWebServicesSectionGroup,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35">
<section name="jsonSerialization"
type="System.Web.Configuration.ScriptingJsonSerializationSection,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" requirePermission="false"
allowDefinition="Everywhere"/>
<section name="profileService"
type="System.Web.Configuration.ScriptingProfileServiceSection,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" requirePermission="false"
allowDefinition="MachineToApplication"/>
<section name="authenticationService"
type="System.Web.Configuration.ScriptingAuthenticationServiceSection,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" requirePermission="false"
allowDefinition="MachineToApplication"/>
</sectionGroup>
</sectionGroup>
</sectionGroup>
</configSections>
<appSettings/>
<connectionStrings>
<add name="ModelCascadingConnectionString"
connectionString="Data Source=SOFTWAR\BIZ;Initial Catalog=ModelCascading;
User ID=sa;Password=123" providerName="System.Data.SqlClient"/>
</connectionStrings>
<system.web>
<!--
Set compilation debug="true" to insert debugging
symbols into the compiled page. Because this
affects performance, set this value to true only
during development.
-->
<pages>
<controls>
<add tagPrefix="asp" namespace="System.Web.UI"
assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
</controls>
</pages>
<compilation debug="true">
<assemblies>
<add assembly="System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
<add assembly="System.Design, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Web.Extensions.Design, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35"/>
<add assembly="System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=B77A5C561934E089"/>
</assemblies>
</compilation>
<!--
The <authentication> section enables configuration
of the security authentication mode used by
ASP.NET to identify an incoming user.
-->
<authentication mode="Windows"/>
<!--
The <customErrors> section enables configuration
of what to do if/when an unhandled error occurs
during the execution of a request. Specifically,
it enables developers to configure html error pages
to be displayed in place of a error stack trace.
<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>
-->
<httpHandlers>
<remove verb="*" path="*.asmx"/>
<add verb="*" path="*.asmx" validate="false"
type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions,
Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add verb="*" path="*_AppService.axd" validate="false"
type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions,
Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add verb="GET,HEAD" path="ScriptResource.axd"
type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions,
Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
validate="false"/>
</httpHandlers>
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
</httpModules>
</system.web>
<system.web.extensions>
<scripting>
<webServices>
<!-- Uncomment this line to customize maxJsonLength and add a custom converter -->
<!--
<jsonSerialization maxJsonLength="500">
<converters>
<add name="ConvertMe" type="Acme.SubAcme.ConvertMeTypeConverter"/>
</converters>
</jsonSerialization>
-->
<!-- Uncomment this line to enable the authentication service. Include requireSSL="true"
if appropriate. -->
<!--
<authenticationService enabled="true" requireSSL = "true|false"/>
-->
<!-- Uncomment these lines to enable the profile service. To allow profile
properties to be retrieved
and modified in ASP.NET AJAX applications, you need to add each property name
to the readAccessProperties and
writeAccessProperties attributes. -->
<!--
<profileService enabled="true"
readAccessProperties="propertyname1,propertyname2"
writeAccessProperties="propertyname1,propertyname2" />
-->
</webServices>
<!--
<scriptResourceHandler enableCompression="true" enableCaching="true" />
-->
</scripting>
</system.web.extensions>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules>
<add name="ScriptModule" preCondition="integratedMode"
type="System.Web.Handlers.ScriptModule, System.Web.Extensions,
Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</modules>
<handlers>
<remove name="WebServiceHandlerFactory-Integrated"/>
<add name="ScriptHandlerFactory" verb="*" path="*.asmx"
preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
<add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd"
preCondition="integratedMode"
type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions,
Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD"
path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler,
System.Web.Extensions, Version=1.0.61025.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35"/>
</handlers>
</system.webServer>
</configuration>